package fuzion24.device.vulnerability.vulnerabilities.framework.zip;

import android.content.Context;

import org.apache.commons.compress.archivers.zip.ModdedZipArchiveOutputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

import fuzion24.device.vulnerability.util.CPUArch;
import fuzion24.device.vulnerability.vulnerabilities.VulnerabilityTest;

/*
  C++   Header   64k Local File header name   Data
      +--------> +----------------------> +---------->
      length=64k classes.dex dex\035\A... dex\035\B...
      +--------> +---------> +---------->
 Java   Header     11 Name       Data
 */
public class ZipBug9950697 implements VulnerabilityTest {

    public String getCVEorID(){
        return "ZipBug 9950697";
    }

    @Override
    public List<CPUArch> getSupportedArchitectures() {
        ArrayList<CPUArch> archs = new ArrayList<>();
        archs.add(CPUArch.ALL);
        return archs;
    }

    public boolean isVulnerable(Context context) throws Exception {
        ByteArrayOutputStream fileNameFunz = new ByteArrayOutputStream();
        ModdedZipArchiveOutputStream zaos = new ModdedZipArchiveOutputStream(fileNameFunz);
        ZipArchiveEntry zae = new ZipArchiveEntry("test_file");
        byte [] originalData = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".getBytes();
        byte [] modifiedData = "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB".getBytes();
        zae.setMethod(ZipEntry.STORED);
        zae.setSize(originalData.length);
        CRC32 checker = new CRC32();
        checker.update(originalData);
        zae.setCrc(checker.getValue());
        zaos.putArchiveEntry(zae, originalData);
        zaos.write(originalData);
        zaos.closeArchiveEntry();
        zaos.writeRaw(modifiedData, 0, modifiedData.length);

        for(int i = 0; i < originalData.length - modifiedData.length; i++){
            zaos.writeRaw(new byte[]{0}, 0, 1);
        }

        zaos.flush();

        List<ZipArchiveEntry> entries = new ArrayList<>();
        entries.add(zae);

        zaos.finish(entries, new ArrayList<ZipArchiveEntry>());

        byte [] testZip = fileNameFunz.toByteArray();

        // write the result to a file
        File outputDir = context.getCacheDir();
        File badZip = File.createTempFile("prefix", "extension", outputDir);
        badZip.deleteOnExit();
        FileOutputStream outstream = new FileOutputStream(badZip);
        outstream.write(testZip);
        outstream.close();

        // see if we can still handle it
        ZipFile bad = new ZipFile(badZip);
        if(bad.size() != 1)
            throw new ZipException("Unexpected number of entries");

        ZipEntry ze = bad.entries().nextElement();
        DataInputStream dis = new DataInputStream(bad.getInputStream(ze));
        byte [] buf = new byte[(int)ze.getSize()];
        dis.readFully(buf);
        bad.close();
        return new String(buf).startsWith("AAAAAAAAAAAAAAA");
    }
}
